﻿//using System;
//using System.Collections;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;

//namespace V2
//{
//    /// <summary> A stateful, efficient Cartesian Product on any number of <see cref="IStreamingSet"/>s.</summary>
//    public class SetProduct : IStreamingSet
//    {
//        private readonly AlignedMembers[] m_MembersByInput;
//        private readonly Set m_ResultSet;

//        private readonly bool[] m_HadValue;
//        public SetProduct(IList<IStreamingSet> input_sets)
//        {
//            m_ResultSet = new Set();
//            m_MembersByInput = new AlignedMembers[input_sets.Count];
//            m_HadValue = new bool[input_sets.Count];

//            for (int i = 0; i < input_sets.Count; i++)
//            {
//                m_MembersByInput[i] = new AlignedMembers(input_sets[i], i, input_sets.Count);
//            }
//            for (int i = 0; i < input_sets.Count; i++)
//            {
//                int input_set_index = i;
//                m_MembersByInput[i].InputStreamingSet.RowBinding +=
//                    delegate(IStreamingSet sender, ChangeArgs args)
//                    {
//                        switch (args.ChangeType)
//                        {
//                            case ChangeType.Add: HandleAddition(input_set_index, sender, args); break;
//                            case ChangeType.Remove: HandleRemoval(input_set_index, sender, args); break;
//                            case ChangeType.Update: HandleUpdate(input_set_index, sender, args); break;
//                            case ChangeType.Clear: HandleClear(input_set_index, sender, args); break;
//                            default: throw new ArgumentOutOfRangeException();
//                        }
//                    };
//            }
//        }
//        public SetProduct(params IStreamingSet[] input_sets) : this((IList<IStreamingSet>)input_sets) { }

//        private Values[] GetEmptyKeyParts()
//        {
//            Values[] valueses = new Values[m_MembersByInput.Length];
//            for (int i = 0; i < valueses.Length; i++)
//            {
//                valueses[i] = Values.EMPTY_VALUES;
//            }
//            return valueses;
//        }
//        #region Add / Remove / Modify / Delete


//        public CombinedValues[] GetAssociatedKeyCombinations(int sender_index, ChangeArgs args)
//        {

//            List<Values> additions = new List<Values>(args.AdditionItems);
//            List<Values>[] source_sets = new List<Values>[m_MembersByInput.Length];

//            // the total number of items we expect from this change.
//            int total_items = additions.Count;
//            for (int current_x = 0; current_x < m_MembersByInput.Length; current_x++)
//            {
//                if (sender_index == current_x)
//                {
//                    source_sets[current_x] = additions;
//                    // total_items already assigned
//                }
//                else
//                {
//                    source_sets[current_x] = m_MembersByInput[current_x].RawInputKeys;
//                    // to change behavior to an inner join, allow multiply by zero.
//                    total_items *= (source_sets[current_x].Count == 0) ? 1 : source_sets[current_x].Count;
//                }
//            }

//            // indices keeps track of the current position being processed.
//            // The position is incremented at the end of the loop.

//            int[] indices = new int[source_sets.Length];
//            CombinedValues[] associated_keys = new CombinedValues[total_items];

//            int output_index = 0;
//            const int TARGET_INDEX = 0; // honestly, this should always be zero, not sure why this should be const even.

//            // this could be rewritten as a single for loop, but I had trouble doing so.

//            while (true)
//            {
//                // this creates all relevant valueses given the assigned indices.
//                // critical to note that it only increments the leftmost index (TARGET_INDEX), in example length=3:
//                // new Values( [0][0][0] ) <-- starting position
//                // new Values( [1][0][0] ) <-- next position
//                // new Values( [2][0][0] ) <-- length of 0 is 3
//                // 
//                // the next super iteration will look like this:
//                // new Values( [0][1][0] )
//                // new Values( [1][1][0] )
//                // new Values( [2][1][0] )
//                for (; indices[TARGET_INDEX] < source_sets[TARGET_INDEX].Count; indices[TARGET_INDEX]++)
//                {
//                    Values[] values_parts = new Values[source_sets.Length];

//                    for (int i = TARGET_INDEX; i < indices.Length; i++)
//                    {
//                        if (source_sets[i].Count == 0) values_parts[i] = Values.EMPTY_VALUES;
//                        else values_parts[i] = source_sets[i][indices[i]];
//                    }
//                    associated_keys[output_index++] = new CombinedValues(values_parts);
//                }
//                indices[TARGET_INDEX] = 0; // now that we're complete, we reset the index

//                // this is our only break condition
//                if (output_index == total_items)
//                {
//                    return associated_keys;
//                }
//#if DEBUG
//                if (output_index > total_items) throw new BadImplementationException("Incorrect logic");
//#endif
//                // Handles INCREMENT, form is as folows:
//                // [0][1][0] <-- starting position
//                // if source_sets[1].Count > 2
//                //  [0][2][0] <-- next position
//                // else
//                //  [0][0][1] <-- (clear previous, then increment next index)
//                for (int inner_x = TARGET_INDEX + 1; inner_x < indices.Length; inner_x++)
//                {
//                    if (indices[inner_x] + 1 < source_sets[inner_x].Count)
//                    {
//                        indices[inner_x]++;
//                        break;
//                    }
//                    else
//                    {
//                        indices[inner_x] = 0;
//                        continue;
//                    }
//                }
//            }

//        }

//        private void HandleAddition(int sender_index, IStreamingSet sender, ChangeArgs args)
//        {

//            // if this is the first addition at sender_index, all previous additions are invalid.
//            if (m_MembersByInput[sender_index].RawInputKeys.Count == 0 && m_ResultSet.IsEmpty == false)
//            {
//                m_ResultSet.Clear();
//            }

//            foreach (Values addition_key in args.AdditionItems)
//            {
//                m_MembersByInput[sender_index].RawInputKeys.Add(addition_key);
//            }
//            CombinedValues[] resulting_keys = GetAssociatedKeyCombinations(sender_index, args);
//            if (resulting_keys.Length == 0)
//            {

//            }
//            ChangeArgs resulting_change = new ChangeArgs(true, resulting_keys);
//            m_ResultSet.EnqueueChange(this, resulting_change);
//        }

//        private void HandleRemoval(int input_set_index, IStreamingSet sender, ChangeArgs args) { throw new NotImplementedException(); }

//        private void HandleUpdate(int input_set_index, IStreamingSet sender, ChangeArgs args) { throw new NotImplementedException(); }

//        private void HandleClear(int input_set_index, IStreamingSet sender, ChangeArgs args) { throw new NotImplementedException(); }


//        #endregion

//        public event ChangeHandler RowBinding
//        {
//            add { m_ResultSet.RowBinding += value; }
//            remove { m_ResultSet.RowBinding -= value; }
//        }

//        /// <summary> The member fields which should be aligned according to the IndexOfInputStreamingSet. </summary>
//        private class AlignedMembers
//        {

//            public readonly IStreamingSet InputStreamingSet;
//            public readonly List<Values> RawInputKeys;
//            public readonly int AssociatedIndex;
//            public AlignedMembers(IStreamingSet input_streaming_set, int index, int total_set_count)
//            {
//                AssociatedIndex = index;
//                InputStreamingSet = input_streaming_set;
//                RawInputKeys = new List<Values>();
//            }
//        }
//    }

//    public class BadImplementationException : Exception
//    {
//        public BadImplementationException(string incorrect_logic) : base(incorrect_logic) { }
//    }
//}
